home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The Atari Compendium
/
The Atari Compendium (Toad Computers) (1994).iso
/
files
/
prgtools
/
programm.ing
/
m2posx10.zoo
/
m2posix.10
/
src
/
proc.ipp
< prev
next >
Encoding:
Amiga
Atari
Commodore
DOS
FM Towns/JPY
Macintosh
Macintosh JP
Macintosh to JP
NeXTSTEP
RISC OS/Acorn
Shift JIS
UTF-8
Wrap
Modula Implementation
|
1993-12-23
|
35.1 KB
|
1,386 lines
IMPLEMENTATION MODULE proc;
__IMP_SWITCHES__
#ifdef HM2
#ifdef __LONG_WHOLE__
(*$!i+: Modul muss mit $i- uebersetzt werden! *)
(*$!w+: Modul muss mit $w- uebersetzt werden! *)
#else
(*$!i-: Modul muss mit $i+ uebersetzt werden! *)
(*$!w-: Modul muss mit $w+ uebersetzt werden! *)
#endif
#endif
(*****************************************************************************)
(* Basiert auf der MiNTLIB von Eric R. Smith und anderen *)
(* --------------------------------------------------------------------------*)
(* 16-Dez-93, Holger Kleinschmidt *)
(*****************************************************************************)
VAL_INTRINSIC
CAST_IMPORT
INLINE_CODE_IMPORT
PTR_ARITH_IMPORT
FROM SYSTEM IMPORT
(* TYPE *) ADDRESS,
(* PROC *) ADR;
FROM PORTAB IMPORT
(* CONST*) NULL,
(* TYPE *) SIGNEDWORD, UNSIGNEDWORD, SIGNEDLONG, UNSIGNEDLONG, ANYLONG,
WORDSET;
FROM MEMBLK IMPORT
(* PROC *) memalloc, memdealloc;
FROM OSCALLS IMPORT
(* PROC *) Pgetpid, Pgetppid, Pgetuid, Pgetgid, Pgeteuid, Pgetegid, Psetuid,
Psetgid, Pgetpgrp, Psetpgrp, Pfork, Pwait3, Pwaitpid, Malloc, Mfree,
Mshrink, Pexec, Pterm, Prusage, Fclose;
FROM ctype IMPORT
(* PROC *) todigit;
FROM cstr IMPORT
(* PROC *) strlen;
FROM pSTRING IMPORT
(* PROC *) COPY, ASSIGN, TOKEN, SLEN, APPEND, APPENDCHR, RPOSCHR, RPOSCHRSET;
FROM cmdline IMPORT
(* VAR *) environ,
(* PROC *) GetEnvVar;
FROM types IMPORT
(* CONST*) EOS, PATHMAX, SUFFIXSEP, DDIRSEP, XDIRSEP,
(* TYPE *) PathName, sizeT, uidT, gidT, pidT, clockT, StrArray, StrPtr,
StrRange, ArrayRange;
IMPORT e;
FROM DosSupport IMPORT
(* CONST*) EXECSUFFIX, DINCR, MinHandle, MaxHandle,
(* TYPE *) FileType, HandleRange,
(* VAR *) FD,
(* PROC *) UnixToDos;
FROM DosSystem IMPORT
(* TYPE *) CmdLine, BasePtr, BasePage,
(* VAR *) BASEP,
(* PROC *) SysClock, DosPid, MiNTVersion;
FROM file IMPORT
(* CONST*) sIFMT, sIFREG,
(* TYPE *) StatRec, modeT,
(* PROC *) stat, close;
(*==========================================================================*)
CONST
EOKL = LIC(0);
CONST
BPSIZE = 256; (* Groesse einer Basepage *)
(* Lokale Umdefinition der Basepage fuer "tfork()" *)
TYPE
BPtr = POINTER TO BPage;
BPage = RECORD
lowtpa : ADDRESS;
hitpa : ADDRESS;
tbase : PROC;
tlen : UNSIGNEDLONG;
dbase : ADDRESS;
dlen : UNSIGNEDLONG;
bbase : ADDRESS;
blen : UNSIGNEDLONG;
dta : ADDRESS;
parent : BPtr;
res1 : UNSIGNEDLONG;
env : ADDRESS;
res2 : ARRAY [0..49] OF ANYLONG;
(* Die restlichen zwei Langworte der Kommandozeile
(die leer ist) dienen als Zwischenspeicher fuer
die Uebergabe des ``Thread'' und dessen Parameter.
*)
tProc : ThreadProc;
tArg : ANYLONG;
END;
TYPE
WaitCode = RECORD
CASE TAG_COLON BOOLEAN OF
FALSE: long : SIGNEDLONG;
|TRUE : pid : UNSIGNEDWORD;
term : SIGNEDWORD;
END;
END;
VAR
MiNT : BOOLEAN;
Stacksize : CARDINAL;
CHILDTIME : UNSIGNEDLONG;
WAITVAL : WaitCode;
errnoADR : ADDRESS;
tforkADR : ADDRESS;
mintADR : ADDRESS;
saveADR : ADDRESS;
#if (defined LPRM2) || (defined SPCM2)
regsave : ARRAY [0..3] OF ADDRESS;
#elif (defined TDIM2)
regsave : ARRAY [0..1] OF ADDRESS;
#elif (defined HM2)
regsave : ARRAY [0..12] OF ADDRESS;
#elif (defined MM2)
regsave : ARRAY [0..10] OF ADDRESS;
#endif
(*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*)
PROCEDURE getpid ( ): pidT;
BEGIN
IF MiNT THEN
RETURN(Pgetpid());
ELSE
RETURN(DosPid(BASEP));
END;
END getpid;
(*---------------------------------------------------------------------------*)
PROCEDURE getppid ( ): pidT;
BEGIN
IF MiNT THEN
RETURN(Pgetppid());
ELSE
RETURN(DosPid(BASEP^.pParent));
END;
END getppid;
(*---------------------------------------------------------------------------*)
PROCEDURE getuid ( ): uidT;
BEGIN
IF MiNT THEN
RETURN(Pgetuid());
ELSE
RETURN(0);
END;
END getuid;
(*---------------------------------------------------------------------------*)
PROCEDURE getgid ( ): gidT;
BEGIN
IF MiNT THEN
RETURN(Pgetgid());
ELSE
RETURN(0);
END;
END getgid;
(*---------------------------------------------------------------------------*)
PROCEDURE geteuid ( ): uidT;
BEGIN
IF MiNT THEN
RETURN(Pgeteuid());
ELSE
RETURN(0);
END;
END geteuid;
(*---------------------------------------------------------------------------*)
PROCEDURE getegid ( ): gidT;
BEGIN
IF MiNT THEN
RETURN(Pgetegid());
ELSE
RETURN(0);
END;
END getegid;
(*---------------------------------------------------------------------------*)
PROCEDURE setuid ((* EIN/ -- *) uid : uidT ): INTEGER;
VAR res : INTEGER;
BEGIN
IF MiNT THEN
IF Psetuid(uid, res) THEN
RETURN(0);
ELSE
IF res = e.eACCDN THEN
e.errno := e.EPERM;
ELSE
e.errno := res;
END;
RETURN(-1);
END;
ELSE
IF uid = 0 THEN
RETURN(0);
ELSE
e.errno := e.EINVAL;
RETURN(-1);
END;
END;
END setuid;
(*---------------------------------------------------------------------------*)
PROCEDURE setgid ((* EIN/ -- *) gid : gidT ): INTEGER;
VAR res : INTEGER;
BEGIN
IF MiNT THEN
IF Psetgid(gid, res) THEN
RETURN(0);
ELSE
IF res = e.eACCDN THEN
e.errno := e.EPERM;
ELSE
e.errno := res;
END;
RETURN(-1);
END;
ELSE
IF gid = 0 THEN
RETURN(0);
ELSE
e.errno := e.EINVAL;
RETURN(-1);
END;
END;
END setgid;
(*---------------------------------------------------------------------------*)
PROCEDURE getpgrp ( ): pidT;
BEGIN
IF MiNT THEN
RETURN(Pgetpgrp());
ELSE
RETURN(DosPid(BASEP));
END;
END getpgrp;
(*---------------------------------------------------------------------------*)
PROCEDURE setpgid ((* EIN/ -- *) pid : pidT;
(* EIN/ -- *) pgid : pidT ): INTEGER;
VAR PID : INTEGER;
BEGIN
IF MiNT THEN
IF (pid < 0) OR (pgid < 0) THEN
e.errno := e.EINVAL;
RETURN(-1);
ELSIF pgid = 0 THEN
pgid := Pgetpid();
END;
IF Psetpgrp(pid, pgid, pgid) THEN
RETURN(0);
ELSE
IF pgid = e.eACCDN THEN
e.errno := e.ESRCH;
ELSE
e.errno := pgid;
END;
RETURN(-1);
END;
ELSE
PID := DosPid(BASEP);
IF ((pid = 0) OR (pid = PID))
AND ((pgid = 0) OR (pgid = PID))
THEN
RETURN(0);
ELSE
e.errno := e.EINVAL;
RETURN(-1);
END;
END;
END setpgid;
(*--------------------------------------------------------------------------*)
PROCEDURE setsid ( ): pidT;
VAR res : INTEGER;
BEGIN
IF MiNT THEN
IF Pgetpgrp() = Pgetpid() THEN
(* Prozess ist bereits ``process group leader'', darf
* kein "setsid()" ausfuehren.
*)
e.errno := e.EPERM;
RETURN(-1);
END;
(* Neue Prozessgruppe hat die Kennung des aufrufenden Prozesses. *)
IF Psetpgrp(0, 0, res) THEN
RETURN(res);
ELSE
IF res = e.eACCDN THEN
e.errno := e.ESRCH;
ELSE
e.errno := res;
END;
RETURN(-1);
END;
ELSE
e.errno := e.EPERM;
RETURN(-1);
END;
END setsid;
(*--------------------------------------------------------------------------*)
PROCEDURE fork ( ): pidT;
VAR res : INTEGER;
BEGIN
IF MiNT THEN
res := Pfork();
IF res >= 0 THEN
RETURN(res);
ELSE
e.errno := res;
RETURN(-1);
END;
ELSE
e.errno := e.ENOSYS;
RETURN(-1);
END;
END fork;
(*---------------------------------------------------------------------------*)
PROCEDURE MakeWaitVal ((* EIN/ -- *) retCode : SIGNEDWORD ): SIGNEDWORD;
CONST SIGINT = 2;
VAR __REG__ exit : UNSIGNEDWORD;
__REG__ sig : UNSIGNEDWORD;
__REG__ ret : WORDSET;
BEGIN
IF retCode = -32 THEN
(* Programm wurde durch 'Ctrl-C' abgebrochen *)
exit := 0;
sig := SIGINT;
ELSE
ret := CAST(WORDSET,retCode);
#if reverse_set
exit := CAST(UNSIGNEDWORD,ret * WORDSET{8..15});
sig := VAL(UNSIGNEDWORD,CAST(UNSIGNEDWORD,ret * WORDSET{1..7}) DIV 256);
#else
exit := CAST(UNSIGNEDWORD,ret * WORDSET{0..7});
sig := VAL(UNSIGNEDWORD,CAST(UNSIGNEDWORD,ret * WORDSET{8..14}) DIV 256);
#endif
END;
IF (sig <> 0) AND (exit <> 0) AND (exit <> 127) THEN
(* normaler Returncode, kein Signal *)
sig := 0;
END;
IF (exit = 127) AND (sig <> 0) THEN
(* Prozess gestoppt *)
RETURN(retCode); (* ist schon entsprechend kodiert *)
ELSE
(* Prozess terminiert, evtl. durch Signal *)
RETURN(VAL(SIGNEDWORD,exit * 256 + sig));
END;
END MakeWaitVal;
(*---------------------------------------------------------------------------*)
PROCEDURE SetStacksize ((* EIN/ -- *) stacksize : CARDINAL);
BEGIN
IF stacksize < MINSTACKSIZE THEN
Stacksize := BPSIZE + MINSTACKSIZE;
ELSE
Stacksize := BPSIZE + stacksize;
END;
END SetStacksize;
(*---------------------------------------------------------------------------*)
#if (defined HM2)
(*$E+ lokale Prozedur als Parameter *)
#endif
PROCEDURE startup;
(* Diese Prozedur ist die erste Anweisung, die nach dem "Pexec()" in
* "tfork()" ausgefuehrt wird. An ihrer Stelle staende normalerweise
* die Initialisierungsroutine eines gestarteten Programms. Aus diesem
* Grund hat auch lediglich Register A7 einen definierten Wert! A7 zeigt
* auf das Ende der TPA, und ueber 4(A7) ist die Adresse der eigenen Basepage
* erreichbar.
* Falls der M2-Compiler beim Beginn der Prozedur erwartet, dass
* bestimmte Register definierte Werte haben (z.B. Megamax: A3 ist der
* Parameterstack!), muessen diese Register entsprechend gesetzt werden,
* bevor auf sie zugegriffen wird.
*)
VAR b : BPtr;
BEGIN
#if (defined MM2)
CODE(202DH,0008H); (* move.l 8(A5),D0 *)
#elif (defined HM2) || (defined TDIM2)
CODE(202EH,0008H); (* move.l 8(A6),D0 *)
#elif (defined LPRM2) || (defined SPCM2)
INLINE(202EH,000CH); (* move.l 12(A6),D0 *)
#endif
GETREGADR(0, b);
#ifdef MM2
(* A3 auf Stackanfang setzen, direkt hinter die Basepage *)
SETREG(11, ADDADR(b, BPSIZE));
#endif
WITH b^ DO
Pterm(tProc(tArg));
END;
END startup;
#if (defined HM2)
(*$E=*)
#endif
(*---------------------------------------------------------------------------*)
PROCEDURE tfork ((* EIN/ -- *) func : ThreadProc;
(* EIN/ -- *) arg : ANYLONG ): INTEGER;
VAR b : BPtr;
pid : SIGNEDLONG;
ret : SIGNEDLONG;
err : INTEGER;
ch : CHAR;
done : BOOLEAN;
void : BOOLEAN;
base : BasePtr;
start : UNSIGNEDLONG;
BEGIN
ch := 0C;
IF Pexec(5, NULL, ADR(ch), NULL, ret) THEN
b := CAST(BPtr,MAKEADR(ret));
void := Mshrink(b, VAL(SIGNEDLONG,Stacksize), err);
WITH b^ DO WITH BASEP^ DO
(* Das Setzen des TPA-Endes ist wichtig fuer das
* nachfolgende "Pexec()", dorthin wird naemlich der
* Stack (A7) des neuen Prozesses gesetzt !
*)
hitpa := ADDADR(b, Stacksize);
tbase := startup;
tlen := pTlen; (* ? *)
bbase := pBbase;
blen := pBlen;
dbase := pDbase;
dlen := pDlen;
(* Parameter in der unbenoetigten Basepage-Kommandozeile uebergeben *)
tProc := func;
tArg := arg;
END; END;
IF MiNT THEN
done := Pexec(104, NULL, b, NULL, pid);
ELSE
(* Programm hat eine neue Basepage, deshalb die alte merken *)
base := BASEP;
BASEP := CAST(BasePtr,b);
start := SysClock();
done := Pexec(4, NULL, b, NULL, pid);
(* Der Speicher fuer Basepage und Environment gehoert dem
* aufrufenden Prozess; er wird deshalb nicht automatisch nach
* Beendigung des Unterprozesses freigegeben. Ein Speicherschutzproblem
* besteht dabei fuer den Unterprozess aber nicht, da dieser Zweig nur
* durchlaufen wird, wenn MiNT, und damit der Speicherschutz, nicht
* aktiv ist.
*)
void := Mfree(b^.env, err);
void := Mfree(b, err);
(* Jetzt gilt wieder die alte Basepage *)
BASEP := base;
IF done THEN
INC(CHILDTIME, SysClock() - start);
WAITVAL.term := VAL(SIGNEDWORD,pid);
(* Aus der Basepageadresse eine Prozesskennung berechnen *)
pid := VAL(SIGNEDLONG,DosPid(b));
WAITVAL.pid := VAL(UNSIGNEDWORD,pid);
END;
END;
IF done THEN
RETURN(INT(pid)); (* Eine gueltige Prozesskennung ist immer positiv *)
ELSE
e.errno := INT(pid);
RETURN(-1);
END;
ELSE
e.errno := INT(ret);
RETURN(-1);
END;
END tfork;
(*---------------------------------------------------------------------------*)
#if (defined LPRM2) || (defined SPCM2)
PROCEDURE vfork ( ): pidT;
BEGIN
(*
movea.l (SP)+,A6 ; alter Framepointer vom Stack retten
movea.l (SP)+,A3 ; alte Modulbasis vom Stack retten
movea.l (SP)+,A1 ; RTN-Adresse vom Stack retten
;; SETREG(8, mintADR);
tst.b (A0)
beq.s tos
move.w #$0113,-(SP) ; Pvfork
trap #1 ;
addq.l #2,SP ;
tst.w D0
bmi.s err
bra.s ende
tos:
nop ;; durch SETREG(8, saveADR); ersetzt
nop ;;
movem.l A1/A3/A5-A6,(A0)
subq.l #2,SP ; Platz fuer Funktionswert
pea child(PC) ; tfork(child, saveADR);
pea (A0) ;
nop ;; durch SETREG(8, tforkADR); ersetzt
nop ;;
jsr (A0)
nop ;; durch SETREG(8, saveADR); ersetzt
nop ;;
move.w (SP)+,D0
movem.l (A0),A1/A3/A5-A6
bmi.s err
bra.s ende
child:
addq.l #4,SP ; RTN-Adresse weg
movea.l (SP)+,A0 ; a0 := saveADR
movem.l (A0),A1/A3/A5-A6
moveq #0,D0
bra.s ende
err:
nop ;; durch SETREG(8, errnoADR); ersetzt
nop ;;
move.w D0,(A0) ; e.errno setzen
moveq #-1,D0
ende:
move.w D0,(SP)
movea.l A3,A4 ; alte Modulbasis setzen
jmp (A1)
*)
CODE(2C5FH,265FH,225FH);
SETREG(8, mintADR);
CODE(4A10H,670EH,3F3CH,0113H,4E41H,548FH,4A40H,6B32H,6038H);
SETREG(8, saveADR);
CODE(48D0H,6A00H,558FH,487AH,0018H,4850H);
SETREG(8, tforkADR);
CODE(4E90H);
SETREG(8, saveADR);
CODE(301FH,4CD0H,6A00H,6B0EH,6014H,588FH,205FH,4CD0H,6A00H,7000H,6008H);
SETREG(8, errnoADR);
CODE(3080H,70FFH,3E80H,284BH,4ED1H);
END vfork;
#elif (defined TDIM2)
__PROCFRAME_OFF__
PROCEDURE vfork ( ): pidT;
BEGIN
(*
movea.l (SP)+,A1 ; RTN-Adresse vom Stack retten
;; SETREG(8, mintADR);
tst.b (A0)
beq.s tos
move.w #$0113,-(SP) ; Pvfork
trap #1 ;
addq.l #2,SP ;
tst.w D0
bmi.s err
bra.s ende
tos:
nop ;; durch SETREG(8, saveADR); ersetzt
nop ;;
nop ;;
movem.l A1/A6,(A0)
subq.l #2,SP ; Platz fuer Funktionswert
pea child(PC) ; tfork(child, saveADR);
pea (A0) ;
nop ;; durch SETREG(8, tforkADR); ersetzt
nop ;;
nop ;;
jsr (A0)
nop ;; durch SETREG(8, saveADR); ersetzt
nop ;;
nop ;;
addq.l #8,SP
move.w (SP)+,D0
movem.l (A0),A1/A6
bmi.s err
bra.s ende
child:
addq.l #4,SP ; RTN-Adresse weg
movea.l (SP)+,A0 ; a0 := saveADR
movem.l (A0),A1/A6
moveq #0,D0
bra.s ende
err:
nop ;; durch SETREG(8, errnoADR); ersetzt
nop ;;
nop ;;
move.w D0,(A0) ; e.errno setzen
moveq #-1,D0
ende:
move.w D0,(SP)
jmp (A1)
*)
CODE(225FH);
SETREG(8, mintADR);
CODE(4A10H,670EH,3F3CH,0113H,4E41H,548FH,4A40H,6B3AH,6042H);
SETREG(8, saveADR);
CODE(48D0H,4200H,558FH,487AH,001EH,4850H);
SETREG(8, tforkADR);
CODE(4E90H);
SETREG(8, saveADR);
CODE(508FH,301FH,4CD0H,4200H,6B0EH,6016H,588FH,205FH,4CD0H,4200H,7000H,600AH);
SETREG(8, errnoADR);
CODE(3080H,70FFH,3E80H,4ED1H);
END vfork;
__PROCFRAME_ON__
#elif (defined HM2)
PROCEDURE vfork ( ): pidT;
BEGIN
(*
; HM
move.l (SP)+,D1 ; Modulbasis vom Stack retten
movea.l (SP)+,A6 ; Frame-Pointer vom Stack retten
movea.l (SP)+,A1 ; RTN-Adresse vom Stack retten
;; SETREG(8, mintADR);
tst.b (A0)
beq.s tos
movea.l D1,A5
move.w #$0113,-(SP) ; Pvfork
trap #1 ;
addq.l #2,SP ;
move.l A5,D1
tst.w D0
bmi.s err
bra.s ende
tos:
nop ;; durch SETREG(8, saveADR); ersetzt
nop ;;
movem.l D1-D7/A1-A6,(A0)
pea (A0)
pea child(PC) ; tfork(child, saveADR);
nop ;; durch SETREG(8, tforkADR); ersetzt
nop ;;
jsr (A0)
nop ;; durch SETREG(8, saveADR); ersetzt
nop ;;
movem.l (A0),D1-D7/A1-A6
tst.w D0
bmi.s err
bra.s ende
child:
addq.l #4,SP ; RTN-Adresse weg
movea.l (SP)+,A0 ; a0 := saveADR
movem.l (A0),D1-D7/A1-A6
moveq #0,D0
bra.s ende
err:
nop ;; durch SETREG(8, errnoADR); ersetzt
nop ;;
#ifdef __LONG_WHOLE__
move.l D0,(A0) ; e.errno setzen
#else
move.w D0,(A0) ; e.errno setzen
#endif
moveq #-1,D0
ende
movea.l D1,A5 ; alte Modulbasis setzen
jmp (A1)
*)
CODE(221FH,2C5FH,225FH);
SETREG(8, mintADR);
CODE(4A10H,6712H,2A41H,3F3CH,0113H,4E41H,548FH,220DH,4A40H,6B30H,6036H);
SETREG(8, saveADR);
CODE(48D0H,7EFEH,4850H,487AH,0016H);
SETREG(8, tforkADR);
CODE(4E90H);
SETREG(8, saveADR);
CODE(4CD0H,7EFEH,4A40H,6B0EH,6014H,588FH,205FH,4CD0H,7EFEH,7000H,6008H);
SETREG(8, errnoADR);
#ifdef __LONG_WHOLE__
CODE(2080H);
#else
CODE(3080H);
#endif
CODE(70FFH,2A41H,4ED1H);
END vfork;
#elif (defined MM2)
#warning *** vfork does not work with MM2
__PROCFRAME_OFF__
PROCEDURE vfork ( ): pidT;
BEGIN
ASSEMBLER
MOVEA.L (A7)+, A1
TST.W MiNT
BEQ.S tos
MOVE.W #$0113, -(A7)
TRAP #1
ADDQ.L #2, A7
TST.W D0
BMI.S err
BRA.S ende
tos:
MOVEM.L D3-D7/A1/A3-A6, regsave
LEA child(PC), A0
MOVE.L A0, (A3)+
MOVE.L #regsave, (A3)+
#ifdef __RES_ON_STACK__
JSR tfork
#ifdef __LONG_WHOLE__
MOVE.L -(A3), D0
#else
MOVE.W -(A3), D0
#endif
#else
JSR tfork/
#endif
TST.W D0
BMI.S err
BRA.S ende
child:
ADDQ.L #4, A7
MOVEA.L -(A3), A0
MOVEM.L (A0), D3-D7/A1/A3-A6
MOVEQ #0, D0
BRA.S ende
err:
#ifdef __LONG_WHOLE__
MOVE.L D0, e.errno
#else
MOVE.W D0, e.errno
#endif
MOVEQ #-1, D0
ende:
#ifdef __RES_ON_STACK__
#ifdef __LONG_WHOLE__
MOVE.L D0, (A3)+
#else
MOVE.W D0, (A3)+
#endif
#endif
JMP (A1)
END;
END vfork;
__PROCFRAME_ON__
#endif
(*---------------------------------------------------------------------------*)
PROCEDURE wait ((* -- /AUS *) VAR state : WaitVal ): pidT;
VAR res : WaitCode;
done : BOOLEAN;
BEGIN
state := WaitVal{};
IF MiNT THEN
done := Pwait3(WORDSET{}, NULL, res.long);
ELSE
res := WAITVAL;
done := res.long >= EOKL;
WAITVAL.long := e.ECHILD;
END;
IF NOT done THEN
e.errno := INT(res.long);
RETURN(-1);
END;
state := CAST(WaitVal,MakeWaitVal(res.term));
RETURN(VAL(pidT,res.pid));
END wait;
(*---------------------------------------------------------------------------*)
PROCEDURE waitpid ((* EIN/ -- *) pid : pidT;
(* -- /AUS *) VAR state : WaitVal;
(* EIN/ -- *) options : WaitOption ): pidT;
VAR res : WaitCode;
done : BOOLEAN;
BEGIN
state := WaitVal{};
IF MiNT THEN
done := Pwaitpid(pid, options, NULL, res.long);
ELSE
IF (pid <> -1) AND (pid <> 0) THEN
e.errno := e.EINVAL;
RETURN(-1);
END;
res := WAITVAL;
done := res.long >= EOKL;
WAITVAL.long := e.ECHILD;
END;
IF NOT done THEN
e.errno := INT(res.long);
RETURN(-1);
END;
state := CAST(WaitVal,MakeWaitVal(res.term));
RETURN(VAL(pidT,res.pid));
END waitpid;
(*---------------------------------------------------------------------------*)
PROCEDURE WIFEXITED ((* EIN/ -- *) state : WaitVal ): BOOLEAN;
BEGIN
RETURN((state * wStopval <> WSTOPPED) AND (state * wTermsig = WaitVal{}));
END WIFEXITED;
(*---------------------------------------------------------------------------*)
PROCEDURE WEXITSTATUS ((* EIN/ -- *) state : WaitVal ): INTEGER;
BEGIN
RETURN(INT(CAST(SIGNEDWORD,state * wRetcode) DIV 256));
END WEXITSTATUS;
(*---------------------------------------------------------------------------*)
PROCEDURE WIFSIGNALED ((* EIN/ -- *) state : WaitVal ): BOOLEAN;
BEGIN
RETURN((state * wStopval <> WSTOPPED) AND (state * wTermsig <> WaitVal{}));
END WIFSIGNALED;
(*---------------------------------------------------------------------------*)
PROCEDURE WTERMSIG ((* EIN/ -- *) state : WaitVal ): CARDINAL;
BEGIN
RETURN(VAL(CARDINAL,CAST(UNSIGNEDWORD,state * wTermsig)));
END WTERMSIG;
(*---------------------------------------------------------------------------*)
PROCEDURE WIFSTOPPED ((* EIN/ -- *) state : WaitVal ): BOOLEAN;
BEGIN
RETURN(state * wStopval = WSTOPPED);
END WIFSTOPPED;
(*---------------------------------------------------------------------------*)
PROCEDURE WSTOPSIG ((* EIN/ -- *) state : WaitVal ): CARDINAL;
BEGIN
RETURN(VAL(CARDINAL,CAST(UNSIGNEDWORD,state * wStopsig) DIV 256));
END WSTOPSIG;
(*---------------------------------------------------------------------------*)
PROCEDURE Spawn ((* EIN/ -- *) mode : SpawnMode;
(* EIN/ -- *) VAR prg : ARRAY OF CHAR;
(* EIN/ -- *) argv : StrArray;
(* EIN/ -- *) envp : StrArray ): INTEGER;
CONST MaxStr = 10;
VAR envPtr : StrPtr;
__REG__ argPtr : StrPtr;
__REG__ cmdIdx : StrRange;
__REG__ envIdx : StrRange;
__REG__ i : ArrayRange;
args : ArrayRange;
envs : ArrayRange;
val : ArrayRange;
pexec : CARDINAL;
res : INTEGER;
lres : SIGNEDLONG;
childStart : UNSIGNEDLONG;
null : BOOLEAN;
done : BOOLEAN;
str : ARRAY [0..MaxStr] OF CHAR;
cmdLine : CmdLine;
fd : HandleRange;
stack : ADDRESS;
msize : CARDINAL;
path0 : StrPtr;
PROCEDURE argcpy (arg : StrPtr; envIdx : StrRange): StrRange;
VAR __REG__ i : StrRange;
__REG__ c : CHAR;
BEGIN
i := 0;
REPEAT
c := arg^[i];
envPtr^[envIdx] := c;
INC(i);
INC(envIdx);
UNTIL c = 0C;
RETURN(envIdx);
END argcpy;
BEGIN
e.errno := 0;
pexec := 0;
IF MiNT THEN
IF mode = pNOWAIT THEN
pexec := 100;
ELSIF mode = pOVERLAY THEN
pexec := 200;
END;
ELSIF mode = pNOWAIT THEN
e.errno := e.EINVAL;
RETURN(-1);
END;
IF (argv = NULL) OR (argv^[0] = NULL) THEN
e.errno := e.EFAULT;
RETURN(-1);
END;
msize := SLEN(prg) + DINCR;
memalloc(VAL(sizeT,msize), stack, path0);
UnixToDos(prg, msize - DINCR, VAL(StrRange,msize), path0, null, done);
IF NOT done THEN
memdealloc(stack);
RETURN(-1);
END;
IF envp = NULL THEN
envp := environ;
END;
(* Laenge des benoetigten Environments berechnen.
* Dazu gehoeren entweder das uebergebene oder das aktuelle
* Environment und die Kommandozeilenargumente einschliesslich
* dem Programmnamen.
*)
lres := 0;
i := 0;
null := FALSE;
WHILE argv^[i] <> NULL DO
res := INT(strlen(argv^[i]));
IF res = 0 THEN
null := TRUE;
(* Bei einem leeren Argument muss der Platz fuer den Index
* in der ARGV-Variable beruecksichtigt werden.
*)
IF i > 1000 THEN
res := 7; (* vier Ziffern & Komma Index + Leerzeichen + Nullbyte *)
ELSIF i > 100 THEN
res := 6;
ELSIF i > 10 THEN
res := 5;
ELSE
res := 4;
END;
ELSE
INC(res); (* wegen Nullbyte *)
END;
INC(i);
INC(lres, VAL(SIGNEDLONG,res));
END;
args := i;
i := 0;
WHILE envp^[i] <> NULL DO
INC(lres, VAL(SIGNEDLONG,strlen(envp^[i]))+VAL(SIGNEDLONG,1));
INC(i);
END;
envs := i;
INC(lres, 20); (* Platz fuer "ARGV=NULL:" & sicherheitshalber etwas mehr *)
(* Benoetigten Speicher anfordern.
* Wenn nicht genuegend Speicher vorhanden ist, mit Fehlermeldung abbrechen.
*)
IF NOT Malloc(lres, envPtr) THEN
e.errno := e.E2BIG;
memdealloc(stack);
RETURN(-1);
END;
envIdx := 0;
(* Das Environment mit den Variablen auffuellen *)
i := 0;
WHILE i < envs DO
envIdx := argcpy(envp^[i], envIdx);
INC(i);
END;
(* Kommandozeile mit ARGV-Verfahren ins Environment schreiben.
* Beginn der eigentlichen Argumente (nach dem Programmnamen) merken,
* fuer die Uebertragung in die Basepage-Kommandozeile.
*)
IF null THEN
str := "ARGV=NULL:";
ELSE
str := "ARGV=";
END;
envIdx := argcpy(CAST(StrPtr,ADR(str)), envIdx);
IF null THEN
DEC(envIdx);
str[MaxStr] := 0C;
i := 0;
WHILE i < args DO
IF argv^[i]^[0] = 0C THEN
cmdIdx := MaxStr - 1;
val := i;
REPEAT
str[cmdIdx] := todigit(VAL(CARDINAL,val MOD 10));
val := val DIV 10;
DEC(cmdIdx);
UNTIL val = 0;
envIdx := argcpy(CAST(StrPtr,ADR(str[cmdIdx+1])), envIdx);
envPtr^[envIdx-1] := ',';
END;
INC(i);
END;
(* das letzte Komma ist zuviel *)
envPtr^[envIdx-1] := 0C;
END;
str := " ";
i := 0;
WHILE i < args DO
IF argv^[i]^[0] = 0C THEN
envIdx := argcpy(CAST(StrPtr,ADR(str)), envIdx);
ELSE
envIdx := argcpy(argv^[i], envIdx);
END;
INC(i);
END;
envPtr^[envIdx] := 0C; (* Ende des Environments kennzeichnen *)
envPtr^[envIdx+1] := 0C; (* Falls es keine Argumente gab *)
(* Soviel der Argumente wie moeglich in die Basepage-Kommandozeile
* uebertragen. ARGV-Verfahren durch den sonst ungueltigen
* Kommandozeilenlaengenwert 127 signalisieren.
*)
cmdLine[0] := CHR(127);
i := 1;
cmdIdx := 1;
WHILE (i < args) AND (cmdIdx <= 124) DO
envIdx := 0;
argPtr := argv^[i]; INC(i);
IF argPtr^[0] = 0C THEN
(* Leeres Argument *)
cmdLine[cmdIdx] := "'";
cmdLine[cmdIdx+1] := "'";
INC(cmdIdx, 2);
ELSE
(* Argument kopieren *)
REPEAT
cmdLine[cmdIdx] := argPtr^[envIdx];
INC(envIdx);
INC(cmdIdx);
UNTIL (argPtr^[envIdx] = 0C) OR (cmdIdx > 124);
END;
(* cmdIdx <= 126 ist gesichert *)
IF i < args THEN
(* Ende des Arguments erreicht *)
cmdLine[cmdIdx] := ' ';
INC(cmdIdx);
ELSE
(* Ende der Argumentliste erreicht *)
cmdLine[cmdIdx] := 0C;
END;
END;
(* Die restliche Kommandozeile wird geloescht. *)
IF cmdIdx > 125 THEN
cmdIdx := 125;
END;
WHILE cmdIdx < 128 DO
cmdLine[cmdIdx] := 0C;
INC(cmdIdx);
END;
(* Unter TOS alle offenen Dateien schliessen, bei denen das 'FdCloExec'-Flag
* gesetzt ist. Kein WITH verwenden, da sonst evtl. keine Registervariable
* fuer Pointer mehr uebrig.
*)
IF NOT MiNT THEN
FOR fd := MinHandle TO MaxHandle DO
IF FD[fd].cloex THEN
done := Fclose(INT(fd), res);
FD[fd].ftype := unknown;
FD[fd].cloex := FALSE;
END;
END;
END;
childStart := SysClock();
done := Pexec(pexec, path0, ADR(cmdLine), envPtr, lres);
INC(CHILDTIME, SysClock() - childStart);
memdealloc(stack);
null := Mfree(envPtr, res);
res := INT(lres);
IF NOT done THEN
(* Wenn "Pexec" selbst fehlschlaegt, gibts einen
* negativen 32-Bit-Wert.
*)
e.errno := res;
RETURN(-1);
ELSIF mode = pOVERLAY THEN
(* Ohne MiNT muss selbst fuer die Beendigung des laufenden
* Prozesses gesorgt werden. Mit MiNT kehrt der ``Pexec''-Aufruf
* erst gar nicht zurueck!
*)
Pterm(res);
ELSIF mode = pWAIT THEN
RETURN(INT(MakeWaitVal(VAL(SIGNEDWORD,res))));
ELSE
(* Bei pNOWAIT wird die (positive) Prozess-ID zurueckgegeben *)
RETURN(res);
END;
END Spawn;
(*---------------------------------------------------------------------------*)
PROCEDURE spawnv ((* EIN/ -- *) mode : SpawnMode;
(* EIN/ -- *) REF prg : ARRAY OF CHAR;
(* EIN/ -- *) argv : StrArray ): INTEGER;
BEGIN
RETURN(Spawn(mode, prg, argv, environ));
END spawnv;
(*---------------------------------------------------------------------------*)
PROCEDURE spawnve ((* EIN/ -- *) mode : SpawnMode;
(* EIN/ -- *) REF prg : ARRAY OF CHAR;
(* EIN/ -- *) argv : StrArray;
(* EIN/ -- *) envp : StrArray ): INTEGER;
BEGIN
RETURN(Spawn(mode, prg, argv, envp));
END spawnve;
(*---------------------------------------------------------------------------*)
PROCEDURE FindExec ((* EIN/ -- *) file : ARRAY OF CHAR;
(* -- /AUS *) VAR path : ARRAY OF CHAR ): BOOLEAN;
(* BUG: Es werden maximal PATHMAX Zeichen aus "PATH" uebernommen. *)
CONST
DEFAULTPATH = ".";
#if no_MIN_MAX
MAXCARD = CAST(CARDINAL,-1);
#else
MAXCARD = MAX(CARDINAL);
#endif
VAR __REG__ sIdx : INTEGER;
__REG__ dIdx : INTEGER;
dtIdx : CARDINAL;
stIdx : CARDINAL;
fLen : CARDINAL;
__REG__ pLen : UNSIGNEDWORD;
l11, l12 : CARDINAL;
l21, l22 : CARDINAL;
st : StatRec;
ext : ARRAY [0..3] OF CHAR;
suffices : PathName;
dirs : PathName;
BEGIN
sIdx := RPOSCHR(0, SUFFIXSEP, file);
dIdx := RPOSCHRSET(0, "\/", file);
IF dIdx >= 0 THEN
(* <file> enthaelt einen Pfad -> nur dort suchen.
* Der Pfad wird aus <file> entfernt.
*)
COPY(0, dIdx, file, dirs);
COPY(dIdx+1, MAXCARD, file, file);
ELSIF NOT GetEnvVar("PATH", dirs) THEN
(* <file> hat keinen Pfad und "PATH" existiert nicht.
* -> nur in 'DEFAULTPATH' suchen.
*)
dirs := DEFAULTPATH;
END;
IF sIdx > dIdx THEN
(* <file> hat eine Extension -> nur diese probieren.
* Die Extension wird aus <file> entfernt.
*)
COPY(sIdx+1, MAXCARD, file, suffices);
COPY(0, sIdx, file, file);
ELSIF NOT GetEnvVar("SUFFIX", suffices) THEN
(* <file> hat keine Extension und "SUFFIX" existiert nicht.
* -> Extensionen aus 'EXECSUFFIX' probieren.
*)
ASSIGN(EXECSUFFIX, suffices);
END;
(* Jetzt enthaelt 'dirs' alle zu durchsuchenden Verzeichnisse,
* 'suffices' alle auszuprobierenden Extensionen und 'file'
* den ``nackten'' Dateinamen ohne Pfad und Extension.
*)
dtIdx := 0; l11 := 0;
(* Jedes Verzeichnis mit allen Extensionen durchprobieren *)
WHILE TOKEN(dirs, ";,", dtIdx, l11, l12, path) DO
pLen := VAL(UNSIGNEDWORD,SLEN(path));
IF (pLen > 0) AND (pLen < PATHMAX-1)
AND (path[pLen-1] <> DDIRSEP) AND (path[pLen-1] <> XDIRSEP)
THEN
path[pLen] := DDIRSEP;
INC(pLen);
path[pLen] := EOS;
END;
APPEND(file, path);
APPENDCHR(".", path);
(* 'path': Pfad + Dateiname + Punkt fuer Extension*)
pLen := VAL(UNSIGNEDWORD,SLEN(path));
stIdx := 0; l21 := 0;
WHILE TOKEN(suffices, ";,", stIdx, l21, l22, ext) DO
(* Jetzt wird probiert, ob eine Datei mit einer der angegebenen
* Extensionen im Verzeichnis existiert. Das 'x-Bit' wird nicht
* beruecksichtigt.
*)
IF ext[0] = EOS THEN
(* Auch ohne Extension versuchen *)
path[pLen-1] := EOS; (* Ohne Punkt *)
ELSE
APPEND(ext, path); (* Extension anhaengen *)
END;
IF (stat(path, st) = 0) AND (st.stMode * sIFMT = sIFREG) THEN
RETURN(TRUE);
END;
path[pLen-1] := '.'; (* Punkt fuer Extension wieder an seinen Platz *)
path[pLen] := EOS; (* Extension wieder entfernen *)
END;
END;
RETURN(FALSE);
END FindExec;
(*---------------------------------------------------------------------------*)
PROCEDURE spawnvp ((* EIN/ -- *) mode : SpawnMode;
(* EIN/ -- *) REF prg : ARRAY OF CHAR;
(* EIN/ -- *) argv : StrArray ): INTEGER;
VAR path0 : PathName;
BEGIN
IF FindExec(prg, path0) THEN
RETURN(Spawn(mode, path0, argv, environ));
ELSE
e.errno := e.ENOENT;
RETURN(-1);
END;
END spawnvp;
(*---------------------------------------------------------------------------*)
PROCEDURE execve ((* EIN/ -- *) REF prg : ARRAY OF CHAR;
(* EIN/ -- *) argv : StrArray;
(* EIN/ -- *) envp : StrArray ): INTEGER;
BEGIN
RETURN(Spawn(pOVERLAY, prg, argv, envp));
END execve;
(*---------------------------------------------------------------------------*)
PROCEDURE execv ((* EIN/ -- *) REF prg : ARRAY OF CHAR;
(* EIN/ -- *) argv : StrArray ): INTEGER;
BEGIN
RETURN(Spawn(pOVERLAY, prg, argv, environ));
END execv;
(*---------------------------------------------------------------------------*)
PROCEDURE execvp ((* EIN/ -- *) REF prg : ARRAY OF CHAR;
(* EIN/ -- *) argv : StrArray ): INTEGER;
VAR path0 : PathName;
BEGIN
IF FindExec(prg, path0) THEN
RETURN(Spawn(pOVERLAY, path0, argv, environ));
ELSE
e.errno := e.ENOENT;
RETURN(-1);
END;
END execvp;
(*---------------------------------------------------------------------------*)
PROCEDURE Exit ((* EIN/ -- *) retval : INTEGER );
BEGIN
Pterm(retval);
END Exit;
(*---------------------------------------------------------------------------*)
PROCEDURE times ((* -- /AUS *) VAR buf : TmsRec ): clockT;
VAR clock : UNSIGNEDLONG;
usage : ARRAY [0..7] OF UNSIGNEDLONG;
BEGIN
clock := SysClock();
IF MiNT THEN
Prusage(ADR(usage));
WITH buf DO
tmsUtime := usage[1] DIV LC(5);
tmsStime := usage[0] DIV LC(5);
tmsCUtime := usage[3] DIV LC(5);
tmsCStime := usage[2] DIV LC(5);
END;
ELSE
WITH buf DO
tmsUtime := VAL(clockT,clock - CHILDTIME);
tmsStime := 0; (* nicht feststellbar *)
tmsCUtime := VAL(clockT,CHILDTIME);
tmsCStime := 0; (* nicht feststellbar *)
END;
END;
RETURN(VAL(clockT,clock));
END times;
(*===========================================================================*)
BEGIN
MiNT := MiNTVersion() > 0;
CHILDTIME := 0;
WAITVAL.long := e.ECHILD;
Stacksize := BPSIZE + MINSTACKSIZE;
errnoADR := ADR(e.errno);
mintADR := ADR(MiNT);
saveADR := ADR(regsave);
#if (defined LPRM2) || (defined SPCM2)
tforkADR := ADR(tfork);
#else
tforkADR := CAST(ADDRESS,tfork);
#endif
END proc.